package itsm.isperp.workflow.service;

import itsm.isperp.framework.data.domain.DataResponse;
import itsm.isperp.framework.utils.JsonBuilder;
import itsm.isperp.framework.web.request.JqGridRequest;
import itsm.isperp.framework.web.utils.DataRequestUtils;
import itsm.isperp.module.entity.bpm.BpmNodeRule;
import itsm.isperp.module.service.bpm.BpmNodeRuleService;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.ActivitiListener;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.ExclusiveGateway;
import org.activiti.bpmn.model.ExtensionAttribute;
import org.activiti.bpmn.model.ExtensionElement;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.ImplementationType;
import org.activiti.bpmn.model.MultiInstanceLoopCharacteristics;
import org.activiti.bpmn.model.ScriptTask;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.ServiceTask;
import org.activiti.bpmn.model.UserTask;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * 
 * 
 * 
 * @author lizx
 * @date 2013-11-29
 * @since 1.0
 * 
 */
@Service
public class BpmBpmnModelService {

	@Autowired
	RepositoryService repositoryService;

	@Autowired
	BpmNodeRuleService bpmNodeRuleService;

	@ResponseBody
	public DataResponse<Model> findAll(JqGridRequest request) {

		return DataRequestUtils.buildFormQuery(request,
				repositoryService.createModelQuery());

	}

	public Model save(String name, String key, String description)
			throws Exception {

		ObjectMapper objectMapper = new ObjectMapper();
		ObjectNode editorNode = objectMapper.createObjectNode();
		editorNode.put("id", "canvas");
		editorNode.put("resourceId", "canvas");
		ObjectNode stencilSetNode = objectMapper.createObjectNode();
		stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
		editorNode.set("stencilset", stencilSetNode);
		Model modelData = repositoryService.newModel();

		ObjectNode modelObjectNode = objectMapper.createObjectNode();
		modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, name);
		modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
		description = StringUtils.defaultString(description);
		modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION,
				description);
		modelData.setMetaInfo(modelObjectNode.toString());
		modelData.setName(name);
		modelData.setKey(StringUtils.defaultString(key));

		repositoryService.saveModel(modelData);
		repositoryService.addModelEditorSource(modelData.getId(), editorNode
				.toString().getBytes("utf-8"));

		return modelData;

	}

	public void deploy(String modelId) throws JsonProcessingException,
			IOException {
		Model modelData = repositoryService.getModel(modelId);
		deploy(modelData);
	}

	public void convertBpmnModel(BpmnModel model)
			throws JsonProcessingException, IOException {

		for (org.activiti.bpmn.model.Process process : model.getProcesses()) {

			ActivitiListener al1 = new ActivitiListener();
			ActivitiListener al2 = new ActivitiListener();
			al1.setEvent("start");
			al1.setImplementationType("class");
			al1.setImplementation("itsm.isperp.workflow.listener.StartEventListener");
			al2.setEvent("end");
			al2.setImplementationType("class");
			al2.setImplementation("itsm.isperp.workflow.listener.EndEventListener");

			process.getExecutionListeners().clear();

			for (FlowElement element : process.getFlowElements()) {

				if (element instanceof UserTask) {
					UserTask ut = (UserTask) element;
					ut.getTaskListeners().clear();
				}
				element.getExecutionListeners().clear();

			}
		}

	}

	public BpmnModel convertToBpmnModel(Model modelData)
			throws JsonProcessingException, IOException {

		JsonNode modelNode = new ObjectMapper().readTree(repositoryService
				.getModelEditorSource(modelData.getId()));
		BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);

		for (org.activiti.bpmn.model.Process process : model.getProcesses()) {

			ActivitiListener al1 = new ActivitiListener();
			ActivitiListener al2 = new ActivitiListener();
			al1.setEvent("start");
			al1.setImplementationType("class");
			al1.setImplementation("itsm.isperp.workflow.listener.StartEventListener");
			al2.setEvent("end");
			al2.setImplementationType("class");
			al2.setImplementation("itsm.isperp.workflow.listener.EndEventListener");

			process.getExecutionListeners().add(al1);
			process.getExecutionListeners().add(al2);

			for (FlowElement element : process.getFlowElements()) {

				if (element instanceof UserTask) {
					UserTask ut = (UserTask) element;

					List<SequenceFlow> sfs = ut.getOutgoingFlows();

					if (sfs.size() > 1) {
						for (SequenceFlow sf : sfs) {

							sf.setConditionExpression("${outcome=='"
									+ sf.getId() + "'}");

						}
					}

					ExtensionElement ue1 = new ExtensionElement();
					ue1.setName("activiti:taskListener");
					ExtensionAttribute createEventEa = new ExtensionAttribute();
					createEventEa.setName("event");
					createEventEa.setValue("create");
					ExtensionAttribute createClassEa = new ExtensionAttribute();
					createClassEa.setName("class");
					createClassEa
							.setValue("itsm.isperp.workflow.listener.TaskSignCreateListener");

					ExtensionElement ue2 = new ExtensionElement();
					ue2.setName("activiti:taskListener");
					ExtensionAttribute assignmentEventEa = new ExtensionAttribute();
					assignmentEventEa.setName("event");
					assignmentEventEa.setValue("assignment");
					ExtensionAttribute assignmentClassEa = new ExtensionAttribute();
					assignmentClassEa.setName("class");
					assignmentClassEa
							.setValue("itsm.isperp.workflow.listener.TaskAssignListener");

					ExtensionElement ue3 = new ExtensionElement();
					ue3.setName("activiti:taskListener");
					ExtensionAttribute completeEventEa = new ExtensionAttribute();
					completeEventEa.setName("event");
					completeEventEa.setValue("complete");
					ExtensionAttribute completeClassEa = new ExtensionAttribute();
					completeClassEa.setName("class");
					completeClassEa
							.setValue("itsm.isperp.workflow.listener.TaskCompleteListener");

					ue1.addAttribute(createEventEa);
					ue1.addAttribute(createClassEa);

					ue2.addAttribute(assignmentEventEa);
					ue2.addAttribute(assignmentClassEa);

					ue3.addAttribute(completeEventEa);
					ue3.addAttribute(completeClassEa);

					List<ExtensionElement> ue1List = new ArrayList<ExtensionElement>();
					ue1List.add(ue1);

					List<ExtensionElement> ue2List = new ArrayList<ExtensionElement>();
					ue2List.add(ue2);

					List<ExtensionElement> ue3List = new ArrayList<ExtensionElement>();
					ue3List.add(ue3);

					if (!ut.getExtensionElements().containsKey("create")) {
						ut.getExtensionElements().put("create", ue1List);
					}

					if (!ut.getExtensionElements().containsKey("assignment")) {
						ut.getExtensionElements().put("assignment", ue2List);
					}

					if (!ut.getExtensionElements().containsKey("complete")) {
						ut.getExtensionElements().put("complete", ue3List);
					}

					MultiInstanceLoopCharacteristics mc = ut
							.getLoopCharacteristics();

					if (mc != null) {

						BpmNodeRule br = bpmNodeRuleService.findOne(ut.getId());

						if (br != null) {
							HashMap<String, String> map = JsonBuilder
									.buildNormalBinder().fromJson(
											br.getRuleContent(), HashMap.class);

							mc.setCompletionCondition(map.get("strComp"));
							mc.setLoopCardinality(map.get("strLoop"));
						}
					}

				} else if (element instanceof ExclusiveGateway) {
					ExclusiveGateway gw = (ExclusiveGateway) element;

					// bug--只能获取一个目标流，将会在5.15中修复（pull161）
					List<SequenceFlow> list = gw.getOutgoingFlows();

					for (SequenceFlow sf : list) {
						BpmNodeRule br = bpmNodeRuleService.findOne(sf.getId());
						if (br != null) {
							sf.setConditionExpression("${"
									+ br.getRuleContent() + "}");
						} else if (StringUtils.isEmpty(sf
								.getConditionExpression()))
							sf.setConditionExpression("${true eq true}");
					}
				} else if (element instanceof ServiceTask) {
					ServiceTask st = (ServiceTask) element;

					st.setType("");
					st.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_CLASS);
					st.setImplementation("itsm.isperp.workflow.listener.MessageServiceTaskListener");

				} else if (element instanceof ScriptTask) {

					ScriptTask st = (ScriptTask) element;
					st.setScript("def i=0;");
					st.setScriptFormat("groovy");

					ExtensionElement ue1 = new ExtensionElement();
					ue1.setName("activiti:executionListener");
					ExtensionAttribute startEventEa = new ExtensionAttribute();
					startEventEa.setName("event");
					startEventEa.setValue("start");
					ExtensionAttribute startClassEa = new ExtensionAttribute();
					startClassEa.setName("class");
					startClassEa
							.setValue("itsm.isperp.workflow.listener.AutoTaskListener");
					List<ExtensionElement> ue1List = new ArrayList<ExtensionElement>();

					ue1.addAttribute(startEventEa);
					ue1.addAttribute(startClassEa);
					ue1List.add(ue1);

					st.getExtensionElements().put("start", ue1List);

				}
			}
		}

		return model;
	}

	public void deploy(Model modelData) throws JsonProcessingException,
			IOException {

		byte[] bpmnBytes = null;

		BpmnModel model = convertToBpmnModel(modelData);

		bpmnBytes = new BpmnXMLConverter().convertToXML(model);

		String processName = modelData.getName();
		DeploymentBuilder db = repositoryService.createDeployment().name(
				modelData.getName());

		ByteArrayInputStream bis = new ByteArrayInputStream(bpmnBytes);

		processName = processName.replaceAll("\\.bpmn20\\.xml", "");
		processName += ".bpmn20.xml";

		db.addInputStream(processName, bis);
		Deployment deployment = db.deploy();

		modelData.setDeploymentId(deployment.getId());

		// 判断是否覆盖现有的流程实例
		//
		ProcessDefinition processDefinition = repositoryService
				.createProcessDefinitionQuery()
				.deploymentId(deployment.getId()).singleResult();

		modelData.setKey(processDefinition.getKey());
		modelData.setName(processDefinition.getResourceName());
		modelData.setCategory(processDefinition.getDeploymentId());

		repositoryService.saveModel(modelData);

	}

	public ByteArrayInputStream export(String modelId)
			throws JsonProcessingException, IOException {

		Model modelData = repositoryService.getModel(modelId);
		BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
		JsonNode editorNode = new ObjectMapper().readTree(repositoryService
				.getModelEditorSource(modelData.getId()));
		BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
		BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
		byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);

		return new ByteArrayInputStream(bpmnBytes);

	}

}
